/*
 * Copyright (C) 2010 CODEBISTRO LLC, Sunnyvale, California.
 * All rights reserved. Proprietary and Confidential.
 */
package org.codebistro.util;

import java.util.Arrays;
import java.util.regex.Pattern;

/**
 * A collection of small utilites that helps to make programs smaller
 */
public class Shortcut14 {
	private static final Pattern INT_NUMBER_PATTERN = Pattern.compile("^[0-9]+$");
	public static final String[] STRINGS_EMPTY = new String[0];
	public static final Long[] LONGS_EMPTY = new Long[0];

	public static final int SEC= 1000;		// from milliseconds
	public static final int MIN= 60 * SEC;	
	public static final int HOUR= 60 * MIN;	
	public static final int DAY= 24 * HOUR;

	public static Long[] parseLongs(final String[] strings, final Long defaultVal) {
		if (strings == null || strings.length == 0) {
			return LONGS_EMPTY;
		}

		final Long[] result = new Long[strings.length];
		for (int index = 0; index < result.length; index++) {
			result[index] = parseLong(strings[index], defaultVal);
		}
		return result;
	}

	public static Integer[] parseIntegers(final String[] strings, final Integer defaultVal) {
		if (strings == null || strings.length == 0) {
			return new Integer[0];
		}

		final Integer[] result = new Integer[strings.length];
		for (int index = 0; index < result.length; index++) {
			result[index] = parseInteger(strings[index], defaultVal);
		}

		return result;
	}

	public static int parseInt(String value, int defaultVal) {
		if (Strings14.isEmpty(value)) {
			return defaultVal;
		}

		try {
			return Integer.parseInt(value);
		} catch (NumberFormatException e) {
			return defaultVal;
		}
	}

	public static Integer parseInteger(String value, Integer defaultVal) {
		if (Strings14.isEmpty(value)) {
			return defaultVal;
		}

		try {
			return new Integer(Integer.parseInt(value));
		} catch (NumberFormatException e) {
			return defaultVal;
		}
	}

	public static double parseDouble(final String value, final int defaultVal) {
		if (Strings14.isEmpty(value)) {
			return defaultVal;
		}

		try {
			return Double.parseDouble(value);
		} catch (NumberFormatException e) {
			return defaultVal;
		}
	}

	public static Double parseDouble(final String value, final Double defaultVal) {
		if (Strings14.isEmpty(value)) {
			return defaultVal;
		}

		try {
			return new Double(Double.parseDouble(value));
		} catch (NumberFormatException e) {
			return defaultVal;
		}
	}

	public static Long parseLong(String value, Long defaultVal) {
		if (Strings14.isEmpty(value)) {
			return defaultVal;
		}

		try {
			return new Long(Long.parseLong(value));
		} catch (NumberFormatException e) {
			return defaultVal;
		}
	}

	public static long parseLong(String value, long defaultVal) {
		if (Strings14.isEmpty(value)) {
			return defaultVal;
		}

		try {
			return Long.parseLong(value);
		} catch (NumberFormatException e) {
			return defaultVal;
		}
	}

	public static Boolean parseBoolean(String value, Boolean defaultVal) {
		if (Strings14.isEmpty(value)) {
			return defaultVal;
		}

		try {
			return Boolean.valueOf(value);
		} catch (NumberFormatException e) {
			return defaultVal;
		}
	}

	public static String nullsafeTrim(final String someString) {
		return someString == null ? null : someString.trim();
	}

	public static String nullSafeToString(Object obj, String defaultValue) {
		if (obj == null) {
			return defaultValue;
		}
		return obj.toString();
	}

	public static String substEmpty(String value, String defaultVal) {
		if (Strings14.isEmpty(value)) {
			return defaultVal;
		}

		return value;
	}

	public static int indexOf(String searchTarget, String[] strings) {
		if (strings == null || strings.length == 0) {
			return -1;
		}

		for (int strIndex = 0; strIndex < strings.length; strIndex++) {
			final String str = strings[strIndex];
			if (nullSafeEq(searchTarget, str)) {
				return strIndex;
			}
		}

		return -1;
	}

	public static int indexOfClass(final Class source, final Class[] instances) {
		for (int strIndex = 0; strIndex < instances.length; strIndex++) {
			final Class inst = instances[strIndex];
			if (Shortcut14.nullSafeEq(source, inst)) {
				return strIndex;
			}
		}

		return -1;
	}

    public static int indexOfIgnoreCase(String searchTarget, String[] strings) {
        if (searchTarget != null) {
            searchTarget = searchTarget.toLowerCase();
        }

        for (int i = 0; i < strings.length; i++) {
            if (strings[i] != null) {
                strings[i] = strings[i].toLowerCase();
            }
        }

        return indexOf(searchTarget, strings);
    }

	/**
	 * Simple enhancement of equals() that performs additional null checks.
	 * It assumess that null references are equal.
	 */
	public static boolean nullSafeEq(final Object objectA, final Object objectB) {
		if (objectA == objectB) {
			return true;
		}

		if (objectA == null && objectB == null) {
			return true;
		}

		if (objectA == null || objectB == null) {
			return false;
		}

		if (objectA instanceof Number && objectB instanceof Number) {
			Number numberA = (Number) objectA;
			Number numberB = (Number) objectB;

			final long aLong = numberA.longValue();
			final double aDouble = numberA.doubleValue();

			final long bLong = numberB.longValue();
			final double bDouble = numberB.doubleValue();

			if (aLong == aDouble && bLong == bDouble) {
				return aLong == bLong;
			} else {
				return aDouble == bDouble;
			}
		}

		if (objectA.getClass().isArray() && objectB.getClass().isArray()) {
			final Class aCompType = objectA.getClass().getComponentType();
			final Class bCompType = objectB.getClass().getComponentType();

			if (!aCompType.equals(bCompType)) {	//	what about inheritance here?
				return false;
			}

			if (String.class.equals(aCompType)) {
				return Arrays.equals((String[]) objectA, (String[]) objectB);
			}
			if (Byte.TYPE.equals(aCompType)) {
				return Arrays.equals((byte[]) objectA, (byte[]) objectB);
			}
			if (Short.TYPE.equals(aCompType)) {
				return Arrays.equals((short[]) objectA, (short[]) objectB);
			}
			if (Integer.TYPE.equals(aCompType)) {
				return Arrays.equals((int[]) objectA, (int[]) objectB);
			}
			if (Long.TYPE.equals(aCompType)) {
				return Arrays.equals((long[]) objectA, (long[]) objectB);
			}
			if (Character.TYPE.equals(aCompType)) {
				return Arrays.equals((char[]) objectA, (char[]) objectB);
			}
			if (Float.TYPE.equals(aCompType)) {
				return Arrays.equals((float[]) objectA, (float[]) objectB);
			}
			if (Double.TYPE.equals(aCompType)) {
				return Arrays.equals((double[]) objectA, (double[]) objectB);
			}
			if (Boolean.TYPE.equals(aCompType)) {
				return Arrays.equals((boolean[]) objectA, (boolean[]) objectB);
			}
			return Arrays.equals((Object[]) objectA, (Object[]) objectB);
		}

		return objectA.equals(objectB);
	}


	public static boolean isEmpty(Object value) {
		return value == null || value.toString().trim().length() <= 0;
	}

	public static boolean isNumber(final String parameter) {
		return !Strings14.isEmpty(parameter) && INT_NUMBER_PATTERN.matcher(parameter).matches();
	}

	/** 
	 * Does nothing but useful for documentation, to collect breakpoints, 
	 * code analysis, etc. 
	 */
	public static void swallow(Throwable ex) {
	}
	
	/** Reliably extract integer value from the Integer object */
	public static int intValue(Object o) {
		if (o instanceof Integer)
			return ((Integer)o).intValue();
		throw new Bark("not an Integer: " + String.valueOf(o));
	}

	/** Reliably extract integer value from the Long object */
	public static long longValue(Object o) {
		if (o instanceof Long)
			return ((Long)o).longValue();
		throw new Bark("not a Long: " + String.valueOf(o));
	}

	public static boolean contains(String[] strings, String string) {
		for (int i= 0; i < strings.length; i++) {
			String string1= strings[i];
			if (nullSafeEq(string, string1)) {
				return true;
			}
		}
		return false;
	}

	public static boolean contains(Object[] objects, Object o) {
		for (int i= 0; i < objects.length; i++) {
			Object oI= objects[i];
			if (nullSafeEq(o, oI)) {
				return true;
			}
		}
		return false;
	}

	public static boolean isPrefixed(final String string, final String[] prefixes) {
		for (int i= 0; i < prefixes.length; i++) {
			String validPrefix= prefixes[i];
			if (string.startsWith(validPrefix)) {
				return true;
			}
		}
		return false;
	}

	public static String renderBytes(byte[] checkSum) {
		final StringBuffer result= new StringBuffer();

		for (int i= 0; i < checkSum.length; i++) {
			byte checkByte= checkSum[i];
			result.append(Integer.toString((checkByte & 0xff) + 0x100, 16).substring(1));
		}

		return result.toString();
	}

	public static byte[] parseBytes(String rendered) {
		byte[] result= new byte[rendered.length() / 2];

		for (int byteIndex= 0; byteIndex < result.length; byteIndex++) {
			String byteStr= rendered.substring(byteIndex * 2, byteIndex * 2 + 2);
			int byteAsSignedInt= Integer.parseInt(byteStr, 16);
			if (0X7f < byteAsSignedInt) {
				result[byteIndex]= (byte) byteAsSignedInt;
			} else {
				result[byteIndex]= (byte) (-0x100 + byteAsSignedInt);
			}
		}

		return result;
	}

	public static String[] toStringArr(final Object[] items) {
		final String[] result = new String[items.length];

		for (int i = 0; i < items.length; i++) {
			result[i] = String.valueOf(items[i]);
		}

		return result;
	}

	public static String[] joinStrings(final String[] itemsA, final String[] itemsB) {
		String[] result = new String[itemsA.length + itemsB.length];

		System.arraycopy(itemsA, 0, result, 0, itemsA.length);
		System.arraycopy(itemsB, 0, result, itemsA.length, itemsB.length);

		return result;
	}

	public static String[][] joinStringArrs(final String[][] itemsA, final String[][] itemsB) {
		String[][] result = new String[itemsA.length + itemsB.length][];

		System.arraycopy(itemsA, 0, result, 0, itemsA.length);
		System.arraycopy(itemsB, 0, result, itemsA.length, itemsB.length);

		return result;
	}

	/**
	 * @see org.codebistro.util.RuntimeFlags
	 */
	public static boolean guessIsProduction() {
		try {
			Class.forName("junit.framework.TestSuite");
			//	it's some testing enviro, as JUnit classes are visible
			return false;
		} catch (Throwable e) {
			return true;
		}
	}

}
